gl renderer: Cache blurred outset shadow nodes
authorTimm Bäder <mail@baedert.org>
Sun, 8 Jul 2018 18:08:15 +0000 (20:08 +0200)
committerTimm Bäder <mail@baedert.org>
Sun, 8 Jul 2018 19:50:59 +0000 (21:50 +0200)
Since these are particularly expensive to render and we have a pretty
big one used in every client-side decorated window.

gsk/gl/gskglrenderer.c
gsk/gl/gskglshadowcache.c [new file with mode: 0644]
gsk/gl/gskglshadowcacheprivate.h [new file with mode: 0644]
gsk/meson.build

index ad2e8c5cf773c4a08fad86457ff758e285e6c543..76a54728deb2b47b98e2f161c37051c9339670e0 100644 (file)
@@ -13,6 +13,7 @@
 #include "gskglglyphcacheprivate.h"
 #include "gskglrenderopsprivate.h"
 #include "gskcairoblurprivate.h"
+#include "gskglshadowcacheprivate.h"
 
 #include "gskprivate.h"
 
@@ -53,7 +54,6 @@
                               glGetUniformLocation(program_ptr->id, "u_" #uniform_basename);\
               }G_STMT_END
 
-
 static void G_GNUC_UNUSED
 print_render_node_tree (GskRenderNode *root, int level)
 {
@@ -266,6 +266,7 @@ struct _GskGLRenderer
   GArray *render_ops;
 
   GskGLGlyphCache glyph_cache;
+  GskGLShadowCache shadow_cache;
 
 #ifdef G_ENABLE_DEBUG
   struct {
@@ -1077,6 +1078,7 @@ render_outset_shadow_node (GskGLRenderer       *self,
   int prev_render_target;
   int texture_id, render_target;
   int blurred_texture_id, blurred_render_target;
+  int cached_tid;
 
   /* offset_outline is the minimal outline we need to draw the given drop shadow,
    * enlarged by the spread and offset by the blur radius. */
@@ -1098,77 +1100,92 @@ render_outset_shadow_node (GskGLRenderer       *self,
   texture_width = offset_outline.bounds.size.width   + blur_extra;
   texture_height = offset_outline.bounds.size.height + blur_extra;
 
-  texture_id = gsk_gl_driver_create_texture (self->gl_driver, texture_width, texture_height);
-  gsk_gl_driver_bind_source_texture (self->gl_driver, texture_id);
-  gsk_gl_driver_init_texture_empty (self->gl_driver, texture_id);
-  render_target = gsk_gl_driver_create_render_target (self->gl_driver, texture_id, FALSE, FALSE);
-
-
-  graphene_matrix_init_ortho (&item_proj,
-                              0, texture_width, 0, texture_height,
-                              ORTHO_NEAR_PLANE, ORTHO_FAR_PLANE);
-  graphene_matrix_scale (&item_proj, 1, -1, 1);
-  graphene_matrix_init_identity (&identity);
+  cached_tid = gsk_gl_shadow_cache_get_texture_id (&self->shadow_cache,
+                                                   self->gl_driver,
+                                                   &offset_outline,
+                                                   blur_radius);
+  if (cached_tid == 0)
+    {
+      texture_id = gsk_gl_driver_create_texture (self->gl_driver, texture_width, texture_height);
+      gsk_gl_driver_bind_source_texture (self->gl_driver, texture_id);
+      gsk_gl_driver_init_texture_empty (self->gl_driver, texture_id);
+      render_target = gsk_gl_driver_create_render_target (self->gl_driver, texture_id, FALSE, FALSE);
+
+
+      graphene_matrix_init_ortho (&item_proj,
+                                  0, texture_width, 0, texture_height,
+                                  ORTHO_NEAR_PLANE, ORTHO_FAR_PLANE);
+      graphene_matrix_scale (&item_proj, 1, -1, 1);
+      graphene_matrix_init_identity (&identity);
+
+      prev_render_target = ops_set_render_target (builder, render_target);
+      op.op = OP_CLEAR;
+      ops_add (builder, &op);
+      prev_projection = ops_set_projection (builder, &item_proj);
+      prev_modelview = ops_set_modelview (builder, &identity);
+      prev_viewport = ops_set_viewport (builder, &GRAPHENE_RECT_INIT (0, 0, texture_width, texture_height));
+
+      /* Draw outline */
+      ops_set_program (builder, &self->color_program);
+      prev_clip = ops_set_clip (builder, &offset_outline);
+      ops_set_color (builder, gsk_outset_shadow_node_peek_color (node));
+      ops_draw (builder, (GskQuadVertex[GL_N_VERTICES]) {
+        { { 0,                            }, { 0, 1 }, },
+        { { 0,             texture_height }, { 0, 0 }, },
+        { { texture_width,                }, { 1, 1 }, },
 
-  prev_render_target = ops_set_render_target (builder, render_target);
-  op.op = OP_CLEAR;
-  ops_add (builder, &op);
-  prev_projection = ops_set_projection (builder, &item_proj);
-  prev_modelview = ops_set_modelview (builder, &identity);
-  prev_viewport = ops_set_viewport (builder, &GRAPHENE_RECT_INIT (0, 0, texture_width, texture_height));
+        { { texture_width, texture_height }, { 1, 0 }, },
+        { { 0,             texture_height }, { 0, 0 }, },
+        { { texture_width,                }, { 1, 1 }, },
+      });
 
-  /* Draw outline */
-  ops_set_program (builder, &self->color_program);
-  prev_clip = ops_set_clip (builder, &offset_outline);
-  ops_set_color (builder, gsk_outset_shadow_node_peek_color (node));
-  ops_draw (builder, (GskQuadVertex[GL_N_VERTICES]) {
-    { { 0,                            }, { 0, 1 }, },
-    { { 0,             texture_height }, { 0, 0 }, },
-    { { texture_width,                }, { 1, 1 }, },
-
-    { { texture_width, texture_height }, { 1, 0 }, },
-    { { 0,             texture_height }, { 0, 0 }, },
-    { { texture_width,                }, { 1, 1 }, },
-  });
-
-  blurred_texture_id = gsk_gl_driver_create_texture (self->gl_driver, texture_width, texture_height);
-  gsk_gl_driver_bind_source_texture (self->gl_driver, blurred_texture_id);
-  gsk_gl_driver_init_texture_empty (self->gl_driver, blurred_texture_id);
-  blurred_render_target = gsk_gl_driver_create_render_target (self->gl_driver, blurred_texture_id, TRUE, TRUE);
-
-  ops_set_render_target (builder, blurred_render_target);
-  op.op = OP_CLEAR;
-  ops_add (builder, &op);
+      blurred_texture_id = gsk_gl_driver_create_permanent_texture (self->gl_driver, texture_width, texture_height);
+      gsk_gl_driver_bind_source_texture (self->gl_driver, blurred_texture_id);
+      gsk_gl_driver_init_texture_empty (self->gl_driver, blurred_texture_id);
+      blurred_render_target = gsk_gl_driver_create_render_target (self->gl_driver, blurred_texture_id, TRUE, TRUE);
 
-  gsk_rounded_rect_init_from_rect (&blit_clip,
-                                   &GRAPHENE_RECT_INIT (0, 0, texture_width, texture_height), 0.0f);
+      ops_set_render_target (builder, blurred_render_target);
+      op.op = OP_CLEAR;
+      ops_add (builder, &op);
 
-  ops_set_program (builder, &self->blur_program);
-  op.op = OP_CHANGE_BLUR;
-  op.blur.size.width = texture_width;
-  op.blur.size.height = texture_height;
-  op.blur.radius = blur_radius;
-  ops_add (builder, &op);
+      gsk_rounded_rect_init_from_rect (&blit_clip,
+                                       &GRAPHENE_RECT_INIT (0, 0, texture_width, texture_height), 0.0f);
 
-  ops_set_clip (builder, &blit_clip);
-  ops_set_texture (builder, texture_id);
-  ops_draw (builder, (GskQuadVertex[GL_N_VERTICES]) {
-    { { 0,             0              }, { 0, 1 }, },
-    { { 0,             texture_height }, { 0, 0 }, },
-    { { texture_width, 0              }, { 1, 1 }, },
+      ops_set_program (builder, &self->blur_program);
+      op.op = OP_CHANGE_BLUR;
+      op.blur.size.width = texture_width;
+      op.blur.size.height = texture_height;
+      op.blur.radius = blur_radius;
+      ops_add (builder, &op);
 
-    { { texture_width, texture_height }, { 1, 0 }, },
-    { { 0,             texture_height }, { 0, 0 }, },
-    { { texture_width, 0              }, { 1, 1 }, },
-  });
+      ops_set_clip (builder, &blit_clip);
+      ops_set_texture (builder, texture_id);
+      ops_draw (builder, (GskQuadVertex[GL_N_VERTICES]) {
+        { { 0,             0              }, { 0, 1 }, },
+        { { 0,             texture_height }, { 0, 0 }, },
+        { { texture_width, 0              }, { 1, 1 }, },
 
+        { { texture_width, texture_height }, { 1, 0 }, },
+        { { 0,             texture_height }, { 0, 0 }, },
+        { { texture_width, 0              }, { 1, 1 }, },
+      });
 
-  ops_set_clip (builder, &prev_clip);
 
-  ops_set_viewport (builder, &prev_viewport);
-  ops_set_modelview (builder, &prev_modelview);
-  ops_set_projection (builder, &prev_projection);
-  ops_set_render_target (builder, prev_render_target);
+      ops_set_clip (builder, &prev_clip);
+      ops_set_viewport (builder, &prev_viewport);
+      ops_set_modelview (builder, &prev_modelview);
+      ops_set_projection (builder, &prev_projection);
+      ops_set_render_target (builder, prev_render_target);
+
+      gsk_gl_shadow_cache_commit (&self->shadow_cache,
+                                  &offset_outline,
+                                  blur_radius,
+                                  blurred_texture_id);
+    }
+  else
+    {
+      blurred_texture_id = cached_tid;
+    }
 
   ops_set_program (builder, &self->outset_shadow_program);
   ops_set_texture (builder, blurred_texture_id);
@@ -1393,8 +1410,6 @@ render_outset_shadow_node (GskGLRenderer       *self,
       }
 
   }
-
-  ops_set_clip (builder, &prev_clip);
 }
 
 static inline void
@@ -2006,6 +2021,7 @@ gsk_gl_renderer_realize (GskRenderer  *renderer,
     return FALSE;
 
   gsk_gl_glyph_cache_init (&self->glyph_cache, renderer, self->gl_driver);
+  gsk_gl_shadow_cache_init (&self->shadow_cache);
 
   return TRUE;
 }
@@ -2030,6 +2046,7 @@ gsk_gl_renderer_unrealize (GskRenderer *renderer)
     glDeleteProgram (self->programs[i].id);
 
   gsk_gl_glyph_cache_free (&self->glyph_cache);
+  gsk_gl_shadow_cache_free (&self->shadow_cache, self->gl_driver);
 
   g_clear_object (&self->gl_profiler);
   g_clear_object (&self->gl_driver);
@@ -2524,6 +2541,7 @@ gsk_gl_renderer_do_render (GskRenderer           *renderer,
 
   gsk_gl_driver_begin_frame (self->gl_driver);
   gsk_gl_glyph_cache_begin_frame (&self->glyph_cache);
+  gsk_gl_shadow_cache_begin_frame (&self->shadow_cache, self->gl_driver);
 
   memset (&render_op_builder, 0, sizeof (render_op_builder));
   render_op_builder.renderer = self;
diff --git a/gsk/gl/gskglshadowcache.c b/gsk/gl/gskglshadowcache.c
new file mode 100644 (file)
index 0000000..8d8960e
--- /dev/null
@@ -0,0 +1,121 @@
+
+#include "gskglshadowcacheprivate.h"
+
+typedef struct
+{
+  GskRoundedRect outline;
+  float blur_radius;
+} CacheKey;
+
+typedef struct
+{
+  int texture_id;
+  guint used : 1;
+} CacheValue;
+
+static gboolean
+key_equal (const void *x,
+           const void *y)
+{
+  const CacheKey *a = x;
+  const CacheKey *b = y;
+
+  return memcmp (&a->outline, &b->outline, sizeof (GskRoundedRect)) == 0 &&
+         a->blur_radius == b->blur_radius;
+}
+
+void
+gsk_gl_shadow_cache_init (GskGLShadowCache *self)
+{
+  self->textures = g_hash_table_new_full (g_direct_hash, key_equal, g_free, g_free);
+}
+
+void
+gsk_gl_shadow_cache_free (GskGLShadowCache *self,
+                          GskGLDriver      *gl_driver)
+{
+  g_hash_table_unref (self->textures);
+  self->textures = NULL;
+}
+
+void
+gsk_gl_shadow_cache_begin_frame (GskGLShadowCache *self,
+                                 GskGLDriver      *gl_driver)
+{
+  GHashTableIter iter;
+  CacheKey *key;
+  CacheValue *value;
+
+  /* We remove all textures with used = FALSE since those have not been used in the
+   * last frame. For all others, we reset the `used` value to FALSE instead and see
+   * if they end up with TRUE in the next call to begin_frame. */
+
+  g_hash_table_iter_init (&iter, self->textures);
+  while (g_hash_table_iter_next (&iter, (gpointer *)&key, (gpointer *)&value))
+    {
+      if (!value->used)
+        {
+          /* Remove */
+          gsk_gl_driver_destroy_texture (gl_driver, value->texture_id);
+          g_hash_table_iter_remove (&iter);
+        }
+      else
+        {
+          value->used = FALSE;
+        }
+    }
+}
+
+/* XXX
+ * The offset origin should always be at 0/0, or the blur radius should just go
+ * away since it defines the origin position anyway?
+ */
+int
+gsk_gl_shadow_cache_get_texture_id (GskGLShadowCache     *self,
+                                    GskGLDriver          *gl_driver,
+                                    const GskRoundedRect *shadow_rect,
+                                    float                 blur_radius)
+{
+  CacheValue *value;
+
+  g_assert (self != NULL);
+  g_assert (gl_driver != NULL);
+  g_assert (shadow_rect != NULL);
+
+  value = g_hash_table_lookup (self->textures,
+                               &(CacheKey){
+                                 *shadow_rect,
+                                 blur_radius
+                               });
+
+  if (value == NULL)
+    return 0;
+
+  value->used = TRUE;
+
+  return value->texture_id;
+}
+
+void
+gsk_gl_shadow_cache_commit (GskGLShadowCache     *self,
+                            const GskRoundedRect *shadow_rect,
+                            float                 blur_radius,
+                            int                   texture_id)
+{
+  CacheKey *key;
+  CacheValue *value;
+
+  g_assert (self != NULL);
+  g_assert (shadow_rect != NULL);
+  g_assert (texture_id > 0);
+
+  key = g_new0 (CacheKey, 1);
+  key->outline = *shadow_rect;
+  key->blur_radius = blur_radius;
+
+  value = g_new0 (CacheValue, 1);
+  value->used = TRUE;
+  value->texture_id = texture_id;
+
+  g_hash_table_insert (self->textures, key, value);
+}
diff --git a/gsk/gl/gskglshadowcacheprivate.h b/gsk/gl/gskglshadowcacheprivate.h
new file mode 100644 (file)
index 0000000..e4f4814
--- /dev/null
@@ -0,0 +1,31 @@
+
+
+#ifndef __GSK_GL_SHADOW_CACHE_H__
+#define __GSK_GL_SHADOW_CACHE_H__
+
+#include <glib.h>
+#include "gskgldriverprivate.h"
+#include "gskroundedrect.h"
+
+typedef struct
+{
+  GHashTable *textures;
+} GskGLShadowCache;
+
+
+void gsk_gl_shadow_cache_init           (GskGLShadowCache     *self);
+void gsk_gl_shadow_cache_free           (GskGLShadowCache     *self,
+                                         GskGLDriver          *gl_driver);
+void gsk_gl_shadow_cache_begin_frame    (GskGLShadowCache     *self,
+                                         GskGLDriver          *gl_driver);
+int  gsk_gl_shadow_cache_get_texture_id (GskGLShadowCache     *self,
+                                         GskGLDriver          *gl_driver,
+                                         const GskRoundedRect *shadow_rect,
+                                         float                 blur_radius);
+void gsk_gl_shadow_cache_commit         (GskGLShadowCache     *self,
+                                         const GskRoundedRect *shadow_rect,
+                                         float                 blur_radius,
+                                         int                   texture_id);
+
+
+#endif
index 2b9115bb2e8c2a3dbf30ceb02f468e8a8a72ce5b..8ad27f7de65231e58e4189e536ac367555859c2e 100644 (file)
@@ -41,7 +41,8 @@ gsk_private_sources = files([
   'gl/gskglglyphcache.c',
   'gl/gskglimage.c',
   'gl/gskgldriver.c',
-  'gl/gskglrenderops.c'
+  'gl/gskglrenderops.c',
+  'gl/gskglshadowcache.c',
 ])
 
 gsk_public_headers = files([